/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mukesh;


import ohos.agp.colors.RgbColor;
import ohos.agp.colors.RgbPalette;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.agp.window.service.DisplayManager;
import ohos.agp.window.service.WindowManager;
import ohos.app.Context;


/**
 * This is the only class of the project. It consists in a custom dialog that shows the GUI
 * used for choosing a color using three sliders or an input field.
 */
public class ColorPicker extends CommonDialog implements Slider.ValueChangedListener {

    private final Context context;

    private Component colorView;
    private Slider alphaSeekBar;
    private Slider redSeekBar;
    private Slider greenSeekBar;
    private Slider blueSeekBar;
    private DirectionalLayout dt_error;
    private TextField hexCode;
    private Image worning;
    private int alpha;
    private int red;
    private int green;
    private int blue;
    private ColorPickerCallback callback;

    private boolean withAlpha;
    private boolean autoclose;


    /**
     * Creator of the class. It will initialize the class with black color as default
     *
     * @param context The reference to the ability where the color picker is called
     */
    public ColorPicker(Context context) {
        super(context);

        this.context = context;
        if (this.context instanceof ColorPickerCallback) {
            callback = (ColorPickerCallback) this.context;
        }

        this.alpha = 255;
        this.red = 0;
        this.green = 0;
        this.blue = 0;

        this.withAlpha = false;
        this.autoclose = false;
    }

    /**
     * Creator of the class. It will initialize the class with the rgb color passed as default
     *
     * @param context The reference to the ability where the color picker is called
     * @param red     Red color for RGB values (0 - 255)
     * @param green   Green color for RGB values (0 - 255)
     * @param blue    Blue color for RGB values (0 - 255)
     *                <p>
     *                If the value of the colors it's not in the right range (0 - 255) it will
     *                be place at 0.
     */

    public ColorPicker(Context context, int red, int green, int blue) {
        this(context);

        this.red = ColorFormatHelper.assertColorValueInRange(red);
        this.green = ColorFormatHelper.assertColorValueInRange(green);
        this.blue = ColorFormatHelper.assertColorValueInRange(blue);
    }

    /**
     * Creator of the class. It will initialize the class with the argb color passed as default
     *
     * @param context The reference to the ability where the color picker is called
     * @param color   ARGB color
     */
    public ColorPicker(Context context, int color) {
        this(context);
        RgbColor rgbColor = new RgbColor(color);

        this.alpha = rgbColor.getAlpha();
        this.red = rgbColor.getRed();
        this.green = rgbColor.getGreen();
        this.blue = rgbColor.getBlue();

        this.withAlpha = this.alpha < 255;
    }

    /**
     * Creator of the class. It will initialize the class with the argb color passed as default
     *
     * @param context The reference to the ability where the color picker is called
     * @param alpha   Alpha value (0 - 255)
     * @param red     Red color for RGB values (0 - 255)
     * @param green   Green color for RGB values (0 - 255)
     * @param blue    Blue color for RGB values (0 - 255)
     *                <p>
     *                If the value of the colors it's not in the right range (0 - 255) it will
     *                be place at 0.
     * @since v1.1.0
     */
    public ColorPicker(Context context, int alpha, int red, int green, int blue) {
        this(context);

        this.alpha = ColorFormatHelper.assertColorValueInRange(alpha);
        this.red = ColorFormatHelper.assertColorValueInRange(red);
        this.green = ColorFormatHelper.assertColorValueInRange(green);
        this.blue = ColorFormatHelper.assertColorValueInRange(blue);

        this.withAlpha = true;
    }

    /**
     * Enable auto-dismiss for the dialog
     */
    public void enableAutoClose() {
        this.autoclose = true;
    }

    /**
     * Disable auto-dismiss for the dialog
     */
    public void disableAutoClose() {
        this.autoclose = false;
    }

    public void setCallback(ColorPickerCallback listener) {
        callback = listener;
    }

    private String text;

    /**
     * Simple onCreate function. Here there is the init of the GUI.
     */
    @Override
    protected void onCreate() {
        super.onCreate();
        Component component = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_materialcolorpicker__layout_color_picker, null, false);
        int width = DisplayManager.getInstance().getDefaultDisplay(context).get().getAttributes().width;
        component.setLayoutConfig(new DependentLayout.LayoutConfig((int) (width * 0.8), DependentLayout.LayoutConfig.MATCH_CONTENT));
        getWindow().setPadding(0, 0, 0, 0);
        setSize((int) (width * 0.8), 1050);
        setTransparent(true);
        setContentCustomComponent(component);
        setAlignment(LayoutAlignment.CENTER);
        getWindow().setInputPanelDisplayType(WindowManager.LayoutConfig.INPUT_ADJUST_PAN);
        colorView = component.findComponentById(ResourceTable.Id_colorView);
        hexCode = (TextField) component.findComponentById(ResourceTable.Id_hexCode);
        alphaSeekBar = (Slider) component.findComponentById(ResourceTable.Id_alphaSeekBar);
        redSeekBar = (Slider) component.findComponentById(ResourceTable.Id_redSeekBar);
        greenSeekBar = (Slider) component.findComponentById(ResourceTable.Id_greenSeekBar);
        blueSeekBar = (Slider) component.findComponentById(ResourceTable.Id_blueSeekBar);
        dt_error = (DirectionalLayout) component.findComponentById(ResourceTable.Id_dt_error);
        worning = (Image) component.findComponentById(ResourceTable.Id_worning);
        findId(component);


        final Button okColor = (Button) component.findComponentById(ResourceTable.Id_okColorButton);
        okColor.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                sendColor();
            }
        });

    }

    private void findId(Component component) {

        alphaSeekBar.setValueChangedListener(this);
        redSeekBar.setValueChangedListener(this);
        greenSeekBar.setValueChangedListener(this);
        blueSeekBar.setValueChangedListener(this);
        text = hexCode.getText();
        hexCode.setEditorActionListener(new Text.EditorActionListener() {
            @Override
            public boolean onTextEditorAction(int action) {

                if (action == InputAttribute.ENTER_KEY_TYPE_GO) {

                    updateColorView(hexCode.getText().toString());
                    return true;
                }

                return false;
            }
        });
        hexCode.addTextObserver(new Text.TextObserver() {
            @Override
            public void onTextUpdated(String str, int i0, int i1, int i2) {
                int length = str.length();
                if (length > 6) {
                    hexCode.setText(text);
                } else {
                    text = str;
                    hexCode.setText(str);
                }
                dt_error.setVisibility(Component.HIDE);
                worning.setVisibility(Component.HIDE);
            }
        });
    }

    private void initUi() {
        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setRgbColor(RgbColor.fromArgbInt(getColor()));
        colorView.setBackground(shapeElement);

        alphaSeekBar.setProgressValue(alpha);
        redSeekBar.setProgressValue(red);
        greenSeekBar.setProgressValue(green);
        blueSeekBar.setProgressValue(blue);
        redSeekBar.setPressState(false);
        greenSeekBar.setPressState(false);
        blueSeekBar.setPressState(false);
        if (!withAlpha) {
            alphaSeekBar.setVisibility(Component.HIDE);
        }

        hexCode.setText(withAlpha
                ? ColorFormatHelper.formatColorValues(alpha, red, green, blue)
                : ColorFormatHelper.formatColorValues(red, green, blue)
        );
    }

    private void sendColor() {
        if (callback != null) {
            callback.onColorChosen(getColor());
        }
        if (autoclose) {
            destroy();
        }
    }

    /**
     * setColor
     *
     * @param color color
     */
    public void setColor(int color) {
        RgbColor rgbColor = new RgbColor(color);
        alpha = rgbColor.getAlpha();
        red = rgbColor.getRed();
        green = rgbColor.getGreen();
        blue = rgbColor.getBlue();
    }

    /**
     * Method that synchronizes the color between the bars, the view, and the HEX code text.
     *
     * @param input HEX Code of the color.
     */
    private void updateColorView(String input) {
        if (input.length() != 6) {
            dt_error.setVisibility(Component.VISIBLE);
            worning.setVisibility(Component.VISIBLE);
        } else {
            dt_error.setVisibility(Component.HIDE);
            worning.setVisibility(Component.HIDE);
            try {
                int parse = RgbPalette.parse('#' + input + "ff");
                RgbColor rgbColor = new RgbColor(parse);
                alpha = rgbColor.getAlpha();
                red = rgbColor.getRed();
                green = rgbColor.getGreen();
                blue = rgbColor.getBlue();
                ShapeElement shapeElement = new ShapeElement();
                shapeElement.setRgbColor(RgbColor.fromArgbInt(getColor()));
                colorView.setBackground(shapeElement);
                redSeekBar.setProgressValue(red);
                greenSeekBar.setProgressValue(green);
                blueSeekBar.setProgressValue(blue);
                redSeekBar.setPressState(false);
                greenSeekBar.setPressState(false);
                blueSeekBar.setPressState(false);

            } catch (IllegalArgumentException ignored) {
                ignored.printStackTrace();
            }
        }

    }


    /**
     * Getter for the ALPHA value of the ARGB selected color
     *
     * @return ALPHA Value Integer (0 - 255)
     * @since v1.1.0
     */
    public int getAlpha() {
        return alpha;
    }

    /**
     * Getter for the RED value of the RGB selected color
     *
     * @return RED Value Integer (0 - 255)
     */
    public int getRed() {
        return red;
    }

    /**
     * Getter for the GREEN value of the RGB selected color
     *
     * @return GREEN Value Integer (0 - 255)
     */
    public int getGreen() {
        return green;
    }


    /**
     * Getter for the BLUE value of the RGB selected color
     *
     * @return BLUE Value Integer (0 - 255)
     */
    public int getBlue() {
        return blue;
    }

    /**
     * setAlpha
     *
     * @param alpha alpha
     */
    public void setAlpha(int alpha) {
        this.alpha = alpha;
    }

    /**
     * setRed
     *
     * @param red red
     */
    public void setRed(int red) {
        this.red = red;
    }

    /**
     * setGreen
     *
     * @param green green
     */
    public void setGreen(int green) {
        this.green = green;
    }

    /**
     * setBlue
     *
     * @param blue blue
     */
    public void setBlue(int blue) {
        this.blue = blue;
    }

    /**
     * setAll
     *
     * @param alpha alpha
     * @param red   red
     * @param green green
     * @param blue  blue
     */
    public void setAll(int alpha, int red, int green, int blue) {
        this.alpha = alpha;
        this.red = red;
        this.green = green;
        this.blue = blue;
    }

    /**
     * setColors
     *
     * @param red   red
     * @param green green
     * @param blue  blue
     */
    public void setColors(int red, int green, int blue) {
        this.red = red;
        this.green = green;
        this.blue = blue;
    }

    /**
     * Getter for the color as ohos Color class value.
     * <p>
     * From ohos Reference: The Color class defines methods for creating and converting color
     * ints.
     * Colors are represented as packed ints, made up of 4 bytes: alpha, red, green, blue.
     * The values are unpremultiplied, meaning any transparency is stored solely in the alpha
     * component, and not in the color components.
     *
     * @return Selected color as ohos Color class value.
     */
    public int getColor() {
        return withAlpha ? Color.argb(alpha, red, green, blue) : Color.rgb(red, green, blue);
    }

    @Override
    public void show() {
        super.show();
        initUi();
    }

    /**
     * Method called when the user change the value of the bars. This sync the colors.
     *
     * @param slider   SeekBar that has changed
     * @param progress The new progress value
     * @param fromUser Whether the user is the reason for the method call
     */
    @Override
    public void onProgressUpdated(Slider slider, int progress, boolean fromUser) {
        slider.invalidate();
        if (slider.getId() == ResourceTable.Id_alphaSeekBar) {
            alpha = progress;
        } else if (slider.getId() == ResourceTable.Id_redSeekBar) {
            red = progress;
            redSeekBar.setPressState(true);
        } else if (slider.getId() == ResourceTable.Id_greenSeekBar) {

            green = progress;
            greenSeekBar.setPressState(true);
        } else if (slider.getId() == ResourceTable.Id_blueSeekBar) {
            blue = progress;
            blueSeekBar.setPressState(true);
        } else {

        }
        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setRgbColor(RgbColor.fromArgbInt(getColor()));
        colorView.setBackground(shapeElement);
        hexCode.setText(withAlpha
                ? ColorFormatHelper.formatColorValues(alpha, red, green, blue)
                : ColorFormatHelper.formatColorValues(red, green, blue)
        );


    }

    /**
     * onTouchStart
     *
     * @param slider slider
     */
    @Override
    public void onTouchStart(Slider slider) {
    }

    /**
     * onTouchEnd
     *
     * @param slider slider
     */
    @Override
    public void onTouchEnd(Slider slider) {
        redSeekBar.setPressState(false);
        greenSeekBar.setPressState(false);
        blueSeekBar.setPressState(false);
    }
}
